
#include "zen_game_dust.h"
#include "zen_game_activity.h"
#include "zen_random.h"

namespace Zen
{
	namespace Game
	{
		static Zen::Random s_rand;

		static inline float sAccMove(float p, float speed, float acc, float time)
		{
			return p + speed * time + acc * time * time * 0.5f;
		}
		static float sDefaultLerp(float s, float e, float v)
		{
			return s + (e - s) * (v * v);
		}
		static float RandomFloat(float midValue, float var)
		{
			float r = midValue + s_rand.NextF<float>(2 * var) - var;
			return r;
		}
		static float RandomFloat0(float midValue, float var)
		{
			float r = RandomFloat(midValue, var);
			return r < 0 ? 0.f : r;
		}
		static float RandomFloat01(float midValue, float var)
		{
			float r = RandomFloat(midValue, var);
			return r < 0 ? 0.f : (r > 1.f ? 1.f : r);
		}

		static Color4f RandomColor01(Color4f midValue, Color4f var)
		{
			return Color4f(RandomFloat01(midValue.red, var.red), RandomFloat01(midValue.green, var.green), RandomFloat01(midValue.blue, var.blue), RandomFloat01(midValue.alpha, var.alpha));
		}
		static Point2 RandomPoint(Point2 midValue, Point2 var)
		{
			return Point2Make(RandomFloat(midValue.x, var.x), RandomFloat(midValue.y, var.y));
		}

		Dust::Dust()
		{
			SetBlend(Zen::Graphics::EBlend::Addition);
		}

		void Dust::SetEmitor(std::shared_ptr<DustEmitor> emitor)
		{
			mEmitor = emitor;
		}

		std::shared_ptr<DustEmitor> Dust::GetEmitor()
		{
			return mEmitor;
		}

		void Dust::SetDustCount(size_t count)
		{
			mTotalCount = count;
		}

		void Dust::SetEmitorLife(float life)
		{
			mEmitorLife = life;
		}
		bool Dust::IsRunning()
		{
			return mIsRunning;
		}

		bool Dust::IsInitialized()
		{
			return mIsInitialized;
		}

		void Dust::RestartEmit()
		{
			mIsRunning = true;
			mIsInitialized = false;
		}

		void Dust::StopEmit()
		{
			mIsRunning = false;
		}

		void Dust::OnReady(std::shared_ptr<RenderStack> stack, float interval)
		{
			if (!mIsRunning || mTotalCount == 0) return;
			if (!mPolygonBuffer) SetPolygonBuffer(NewBuffer(), 2);
			if (!mColorBuffer) SetColorBuffer(NewBuffer());
			if (!mPointSizeBuffer) SetPointSizeBuffer(NewBuffer());

			SetVertexCount(mTotalCount);

			if (!mIsInitialized)
			{
				mEmitor->Initialize(mTotalCount);
				mIsInitialized = true;
				mEmitor->Update(0.f);

				auto& v = mEmitor->GetVertexData();
				mPolygonBuffer->Initialize(sizeof(Point2) * mTotalCount, v.positions.data());
				mColorBuffer->Initialize(sizeof(Color4f) * mTotalCount, v.colors.data());
				mPointSizeBuffer->Initialize(sizeof(float) * mTotalCount, v.sizes.data());
			}
			else
			{
				mEmitor->Update(interval);

				auto& v = mEmitor->GetVertexData();
				mPolygonBuffer->Update(0, sizeof(Point2) * mTotalCount, v.positions.data());
				mColorBuffer->Update(0, sizeof(Color4f) * mTotalCount, v.colors.data());
				mPointSizeBuffer->Update(0, sizeof(float) * mTotalCount, v.sizes.data());
			}

			FigurePointTexture::OnReady(stack, interval);
		}

		void Dust::OnDraw()
		{
			if (!mIsRunning || mTotalCount == 0) return;
			FigurePointTexture::OnDraw();
		}

		GravityDustEmitor::GravityDustEmitor(GravityDustConfig const& config)
		{
			mConfig = config;
			if (!mConfig.alpha_lerp_func) mConfig.alpha_lerp_func = &sDefaultLerp;
			if (!mConfig.color_lerp_func) mConfig.color_lerp_func = &sDefaultLerp;
			if (!mConfig.size_lerp_func) mConfig.size_lerp_func = &sDefaultLerp;
		}

		void GravityDustEmitor::Initialize(size_t count)
		{
			mParticles.resize(count);
			mVertexes.Resize(count);
			mEmitorAge = 0;
			for (size_t i = 0; i < count; ++i)
			{
				this->ResetParticle(i);
				mParticles.at(i).birth = RandomFloat(0, mParticles.at(i).life);
				mEmitorAge = std::max(mEmitorAge, mParticles.at(i).birth);
			}
		}

		void GravityDustEmitor::Update(float interval)
		{
			mEmitorAge += interval;
			for (size_t i = 0; i < mParticles.size(); ++i)
			{
				this->UpdateParticle(i);
			}
		}

		void GravityDustEmitor::ResetParticle(size_t nth)
		{
			auto& p = mParticles.at(nth);
			p.birth = mEmitorAge;
			p.life = RandomFloat0(mConfig.par_life, mConfig.par_life_var);
			p.alpha0 = RandomFloat01(mConfig.start_alpha, mConfig.start_alpha_var);
			p.alpha1 = RandomFloat01(mConfig.end_alpha, mConfig.end_alpha_var);
			p.color0.red = RandomFloat01(mConfig.start_color.red, mConfig.start_color_var.red);
			p.color0.green = RandomFloat01(mConfig.start_color.green, mConfig.start_color_var.green);
			p.color0.blue = RandomFloat01(mConfig.start_color.blue, mConfig.start_color_var.blue);
			p.color0.alpha = RandomFloat01(mConfig.start_color.alpha, mConfig.start_color_var.alpha);
			p.color1.red = RandomFloat01(mConfig.end_color.red, mConfig.end_color_var.red);
			p.color1.green = RandomFloat01(mConfig.end_color.green, mConfig.end_color_var.green);
			p.color1.blue = RandomFloat01(mConfig.end_color.blue, mConfig.end_color_var.blue);
			p.color1.alpha = RandomFloat01(mConfig.end_color.alpha, mConfig.end_color_var.alpha);
			p.size0 = RandomFloat0(mConfig.start_size, mConfig.start_size_var);
			p.size1 = RandomFloat0(mConfig.end_size, mConfig.end_size_var);
			p.emit_position.x = RandomFloat(mConfig.emit_pos.x, mConfig.emit_pos_var.x);
			p.emit_position.y = RandomFloat(mConfig.emit_pos.y, mConfig.emit_pos_var.y);

			auto gv = RandomFloat(mConfig.gravity_acc, mConfig.gravity_acc_var);
			auto gr = RandomFloat(mConfig.gravity_radians, mConfig.gravity_radians_var);
			p.gravity.x = gv * cosf(gr);
			p.gravity.y = gv * sinf(gr);
			auto sv = RandomFloat(mConfig.emit_speed, mConfig.emit_speed_var);
			auto sr = RandomFloat(mConfig.emit_angle, mConfig.emit_angle_var);
			p.emit_speed.x = sv * cosf(sr);
			p.emit_speed.y = sv * sinf(sr);
		}

		void GravityDustEmitor::UpdateParticle(size_t nth)
		{
			auto& v = mVertexes;
			auto& p = mParticles.at(nth);
			if (mEmitorAge > p.birth + p.life)
			{
				ResetParticle(nth);
			}
			float age = (mEmitorAge - p.birth);
			float stage = age / p.life;
			auto& color = v.colors[nth];
			color.red = mConfig.color_lerp_func(p.color0.red, p.color1.red, stage);
			color.green = mConfig.color_lerp_func(p.color0.green, p.color1.green, stage);
			color.blue = mConfig.color_lerp_func(p.color0.blue, p.color1.blue, stage);
			color.alpha = mConfig.color_lerp_func(p.color0.alpha, p.color1.alpha, stage) * mConfig.alpha_lerp_func(p.alpha0, p.alpha1, stage);
			v.sizes[nth] = mConfig.size_lerp_func(p.size0, p.size1, stage);
			auto& position = v.positions[nth];
			position.x = p.emit_position.x + p.emit_speed.x * age + p.gravity.x * age * age * 0.5f;
			position.y = p.emit_position.y + p.emit_speed.y * age + p.gravity.y * age * age * 0.5f;
		}

		DustEmitor::Vertex const& DustEmitor::GetVertexData() const
		{
			return mVertexes;
		}

		float DustEmitor::GetEmitorAge() const
		{
			return mEmitorAge;
		}

	} // namespace Game
} // namespace Zen
